
#include <BKeyEvent>
#include <BCharEvent>
#include <BEvent>
#include <BFocusEvent>
#include <BMouseEvent>
#include <BChangeEvent>
#include <BDragEvent>
#include <BDrag>
#include <BOpenGL>

#include "IGlobal.h"
#include "member_BWidget.h"
#include "member_BObject.h"
#include "member_BLayout.h"
#include "member_BStyle.h"

using namespace BWE;

member_BWidget::member_BWidget(BWidget* widget)
{
	boss = widget;
	window = 0;
	display = 0;
	layout = 0;
	realStyle = 0;

	visible = true;
	refresh = true;
	dirty = true;
	fbo_resized = true;
	fbo = 0;
	rbo_depth = 0;
	tbo = 0;

	modal = false;
	focus = false;
	floating = false;
	droppable = false;
	focusable = true;
	parent = 0;
	border = 1;
	perchs = 0;
	margins = 0;
	sizeStretch = 1.0f;
	sizePolicy.setPolicy(Policy_Dynamic, Policy_Dynamic);
	size.set(100, 100);
	minSize.set(0, 0);
	maxSize.set(999999, 999999);
	dirtyRect = true;
}
member_BWidget::~member_BWidget()
{
	if (tbo)
		glDeleteTextures(1, &tbo);
	if (rbo_depth)
		glDeleteRenderbuffers(1, &rbo_depth);
	if (fbo)
		glDeleteFramebuffers(1, &fbo);
}

void member_BWidget::frame()
{
	if (fbo_resized)
	{
		GLint backup_frame_buffer;
		glGetIntegerv(GL_FRAMEBUFFER_BINDING, &backup_frame_buffer);

		int width = size.width();
		int height = size.height();

		if (!fbo)
		{
			glGenFramebuffers(1, &fbo);
			glGenRenderbuffers(1, &rbo_depth);
		}
		if (fbo)
		{
			glBindFramebuffer(GL_FRAMEBUFFER, fbo);
			glBindRenderbuffer(GL_RENDERBUFFER, rbo_depth);
			glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo_depth);
		}
		if (!tbo)
		{
			glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
			glGenTextures(1, &tbo);
		}
		if (tbo)
		{
			glBindTexture(GL_TEXTURE_2D, tbo);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0);
			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tbo, 0);
		}
		fbo_resized = false;

		glBindFramebuffer(GL_FRAMEBUFFER, backup_frame_buffer);
	}

	if (fbo)
	{
		GLint backup_frame_buffer;
		glGetIntegerv(GL_FRAMEBUFFER_BINDING, &backup_frame_buffer);

		glBindFramebuffer(GL_FRAMEBUFFER, fbo);
		glDrawBuffer(GL_COLOR_ATTACHMENT0);

		glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
		glClear(GL_COLOR_BUFFER_BIT);
		glDisable(GL_LIGHTING);
		glDisable(GL_DEPTH_TEST);

		glEnable(GL_BLEND);
		glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA, GL_DST_ALPHA);
		//glBlendEquation(GL_FUNC_ADD);

		paint.font = boss->font();
		paint.style = boss->realStyle();
		BEvent event(Event_Paint);
		this->procEvent(event);
		dirty = false;

		glBindFramebuffer(GL_FRAMEBUFFER, backup_frame_buffer);

		boss->emit(Signal_Fresh);
	}
}

void member_BWidget::syncWindowPos(const BPoint& wpos)
{
	if (pos != wpos)
	{
		pos = wpos;
		BEvent moveEvent(Event_Move);
		procEvent(moveEvent);
		//boss->emit(Signal_Moved, pos);
	}
}
void member_BWidget::syncWindowSize(const BSize& wsize)
{
	BSize temp = wsize;
	temp.limit(minSize, maxSize);
	if (size != temp)
	{
		size = temp;
		dirtyRect = true;
		fbo_resized = true;
		riseAdjust();
		BEvent resizeEvent(Event_Resize);
		procEvent(resizeEvent);
		boss->emit(Signal_Resize, size);
	}
}

void member_BWidget::procEvent(const BEvent& event)
{
	if (event.type() == Event_Adjust)
	{
		boss->fresh();
	}
	if (event.type() == Event_Resize)
	{
		boss->fresh();
	}
	if (event.type() == Event_Style)
	{
		applyStyle(boss->realStyle());
		boss->fresh();
	}
	if (event.type() == Event_Fresh)
	{
		freshRects();
		adjustLayouts();
	}
	boss->event(event);
	if (droppable)
	{
		const BDragEvent& de = (const BDragEvent&)event;
		switch (event.type())
		{
		case Event_DragDrop:
			boss->emit(Signal_DragDrop, de.drag());
			break;
		case Event_DragEnter:
			boss->emit(Signal_DragEnter, de.drag());
			break;
		case Event_DragMove:
			boss->emit(Signal_DragMove, de.drag());
			break;
		case Event_DragLeave:
			boss->emit(Signal_DragLeave, de.drag());
			break;
		default:
			break;
		}
	}
}

void member_BWidget::applyStyleDriver()
{
	BWidget* widget = boss;
	while (widget)
	{
		IStyleDriver* driver = widget_member(widget)->styleDriver;
		if (driver)
			driver->apply(boss);
		widget = widget_member(widget)->parent;
	}
	if (app_styleDriver)
		app_styleDriver->apply(boss);
	for (int i = 0; i < children.size(); i++)
	{
		BWidget* child = children[i];
		widget_member(child)->applyStyleDriver();
	}
}
void member_BWidget::freshStyleTree()
{
	BStyle* lower = app_style;
	const BWidget* p = parent;
	while (p)
	{
		if (widget_member(p)->style)
		{
			lower = widget_member(p)->style;
			break;
		}
		p = p->parent();
	}
	if (style)
	{
		realStyle = 0;
		style_member(style)->lower = lower;
	}
	else
	{
		realStyle = lower;
	}

	BEvent event(Event_Style);
	this->procEvent(event);

	for (int i = 0; i < children.size(); i++)
	{
		BWidget* child = children[i];
		widget_member(child)->freshStyleTree();
	}
}
void member_BWidget::applyStyle(const BStyle* realStyle)
{
	if (style)
	{
		bool adjusted = false;
		const BValue& widthValue = style->value(Value_Width);
		if (widthValue.valid())
		{
			int width = widthValue;
			boss->setFixedWidth(width);
		}
		const BValue& heightValue = style->value(Value_Height);
		if (heightValue.valid())
		{
			int height = heightValue;
			boss->setFixedHeight(height);
		}
		const BValue& borderValue = style->value(Value_Border);
		if (borderValue.valid())
		{
			border = borderValue;
			adjusted = true;
		}
		const BValue& perchLeftValue = style->value(Value_Perch_Left);
		if (perchLeftValue.valid())
		{
			perchs.left() = perchLeftValue;
			adjusted = true;
		}
		const BValue& perchRightValue = style->value(Value_Perch_Right);
		if (perchRightValue.valid())
		{
			perchs.right() = perchRightValue;
			adjusted = true;
		}
		const BValue& perchTopValue = style->value(Value_Perch_Top);
		if (perchTopValue.valid())
		{
			perchs.top() = perchTopValue;
			adjusted = true;
		}
		const BValue& perchBottomValue = style->value(Value_Perch_Bottom);
		if (perchBottomValue.valid())
		{
			perchs.bottom() = perchBottomValue;
			adjusted = true;
		}
		const BValue& marginLeftValue = style->value(Value_Margin_Left);
		if (marginLeftValue.valid())
		{
			margins.left() = marginLeftValue;
			adjusted = true;
		}
		const BValue& marginTopValue = style->value(Value_Margin_Top);
		if (marginTopValue.valid())
		{
			margins.top() = marginTopValue;
			adjusted = true;
		}
		const BValue& marginRightValue = style->value(Value_Margin_Right);
		if (marginRightValue.valid())
		{
			margins.right() = marginRightValue;
			adjusted = true;
		}
		const BValue& marginBottomValue = style->value(Value_Margin_Bottom);
		if (marginBottomValue.valid())
		{
			margins.bottom() = marginBottomValue;
			adjusted = true;
		}
		if (adjusted)
		{
			dirtyRect = true;
			BEvent event(Event_Adjust);
			this->procEvent(event);
		}
	}
}

void member_BWidget::freshRects()
{
	if (dirtyRect)
	{
		int b = border;
		int w = size.width();
		int h = size.height();

		int pleft = perchs.left();
		int pright = perchs.right();
		int ptop = perchs.top();
		int pbottom = perchs.bottom();

		int mleft = margins.left();
		int mright = margins.right();
		int mtop = margins.top();
		int mbottom = margins.bottom();

		int cx = b + pleft + mleft;
		int cy = b + ptop + mtop;
		int cw = w - b - pleft - mleft - mright - pright - b;
		int ch = h - b - ptop - mtop - mbottom - pbottom - b;

		rectTitle.set(b, b, w - b - b, ptop);
		rectClient.set(cx, cy, cw, ch);
		rectTop.set(b + pleft, b, w - b - pleft - b - pright, ptop);
		rectRight.set(w - b - pright, b + ptop, pright, h - b - b - ptop - pbottom);
		rectLeft.set(b, b + ptop, pleft, h - b - b - ptop - pbottom);
		rectBottom.set(b + pleft, h - b - pbottom, w - b - b - pleft - pright, pbottom);

		dirtyRect = false;

		BEvent adjustEvent(Event_Adjust);
		boss->adjustEvent(adjustEvent);
		boss->emit(Signal_Adjust);
		boss->fresh();
	}
}
void member_BWidget::riseAdjust()
{
	if (titleLayout)
	{
		layout_member(titleLayout)->dirty = true;
	}
	if (mainLayout)
	{
		layout_member(mainLayout)->dirty = true;
	}
	if (leftLayout)
	{
		layout_member(leftLayout)->dirty = true;
	}
	if (rightLayout)
	{
		layout_member(rightLayout)->dirty = true;
	}
	if (topLayout)
	{
		layout_member(topLayout)->dirty = true;
	}
	if (bottomLayout)
	{
		layout_member(bottomLayout)->dirty = true;
	}
	if (parent)
	{
		widget_member(parent)->riseAdjust();
	}
	boss->fresh();
}
void member_BWidget::adjustLayouts()
{
	bool adjusted = false;
	if (titleLayout && layout_member(titleLayout)->dirty)
	{
		titleLayout->setRect(rectTitle);
		titleLayout->adjust();
		adjusted = true;
	}
	if (mainLayout && layout_member(mainLayout)->dirty)
	{
		mainLayout->setRect(rectClient);
		mainLayout->adjust();
		adjusted = true;
	}
	if (leftLayout && layout_member(leftLayout)->dirty)
	{
		leftLayout->setRect(rectLeft);
		leftLayout->adjust();
		adjusted = true;
	}
	if (rightLayout && layout_member(rightLayout)->dirty)
	{
		rightLayout->setRect(rectRight);
		rightLayout->adjust();
		adjusted = true;
	}
	if (topLayout && layout_member(topLayout)->dirty)
	{
		topLayout->setRect(rectTop);
		topLayout->adjust();
		adjusted = true;
	}
	if (bottomLayout && layout_member(bottomLayout)->dirty)
	{
		bottomLayout->setRect(rectBottom);
		bottomLayout->adjust();
		adjusted = true;
	}
	for (int i = 0; i < freeLayouts.size(); i++)
	{
		BLayout* layout = freeLayouts[i];
		if (layout_member(layout)->dirty)
		{
			layout->adjust();
			adjusted = true;
		}
	}
	if (adjusted)
	{
		BEvent event(Event_Adjust);
		this->procEvent(event);
		boss->emit(Signal_Adjust);
	}

}
